package com.aptech.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;


public class BaseBeanConverter {
	
	/**
	 * @param xmlCal
	 * @return
	 */
	protected static Date convertXMLDate(XMLGregorianCalendar xmlCal) {
		if (xmlCal == null)
			return null;
		GregorianCalendar cal = xmlCal.toGregorianCalendar();
		return DateUtils.getCalendarDate(cal);
	}
	
	/**
	 * @param date
	 * @return
	 */
	protected static XMLGregorianCalendar convertToXMLDate(Date date) {
		if (date == null)
			return null;
		XMLGregorianCalendar xmlCal = null;
		try {
			GregorianCalendar cal = (GregorianCalendar)DateUtils.getCalendar(date);
			xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
		} catch (DatatypeConfigurationException e) {
			
		}
		return xmlCal;
	}
	
	public static <S, T> void copyProperties(S source, T target) {
		copyProperties(source, target, null);
	}
	
	@SuppressWarnings("unchecked")
	public static <S, T> void copyProperties(S source, T target, String[] ignoreProperties) {
		Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class actualEditable = target.getClass();
        PropertyDescriptor targetPds[] = BeanUtils.getPropertyDescriptors(actualEditable);
        List ignoreList = ignoreProperties == null ? null : Arrays.asList(ignoreProperties);
        
        for(int i = 0; i < targetPds.length; i++)
        {
        	PropertyDescriptor targetPd = targetPds[i];
            if(targetPd.getWriteMethod() == null || ignoreProperties != null && ignoreList.contains(targetPd.getName()))
                continue;
            PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
            if(sourcePd == null || sourcePd.getReadMethod() == null)
                continue;
            
            try {
            	
                Method readMethod = sourcePd.getReadMethod();
                if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()))
                    readMethod.setAccessible(true);
                Object value = readMethod.invoke(source, new Object[0]);
                Method writeMethod = targetPd.getWriteMethod();
                if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()))
                    writeMethod.setAccessible(true);
                
                if (targetPd.getPropertyType() == Date.class && sourcePd.getPropertyType() == XMLGregorianCalendar.class) {
                	writeMethod.invoke(target, new Object[] {
                			convertXMLDate((XMLGregorianCalendar)value)
    	                });
                } else if (targetPd.getPropertyType() == XMLGregorianCalendar.class && sourcePd.getPropertyType() == Date.class) {
                	writeMethod.invoke(target, new Object[] {
                			convertToXMLDate((Date)value)
    	                });
                } else {
                	// default
	                writeMethod.invoke(target, new Object[] {
	                    value
	                });
                }
            } catch(Throwable ex) {
                throw new FatalBeanException("Could not copy properties from source to target", ex);
            }
        }
	}
	
}
